package org.proteored.miapeapi.xml.mzidentml;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.proteored.miapeapi.cv.Accession;
import org.proteored.miapeapi.cv.ControlVocabularyManager;
import org.proteored.miapeapi.cv.ControlVocabularyTerm;
import org.proteored.miapeapi.cv.msi.Score;
import org.proteored.miapeapi.interfaces.msi.IdentifiedPeptide;
import org.proteored.miapeapi.interfaces.msi.IdentifiedProtein;
import org.proteored.miapeapi.interfaces.msi.InputData;
import org.proteored.miapeapi.interfaces.msi.PeptideModification;
import org.proteored.miapeapi.interfaces.msi.PeptideScore;
import org.proteored.miapeapi.xml.mzidentml.autogenerated.FuGECommonOntologyParamType;
import org.proteored.miapeapi.xml.mzidentml.autogenerated.PSIPIAnalysisProcessPeptideEvidenceType;
import org.proteored.miapeapi.xml.mzidentml.autogenerated.PSIPIAnalysisProcessProteinDetectionHypothesisType;
import org.proteored.miapeapi.xml.mzidentml.autogenerated.PSIPIAnalysisSearchDBSequenceType;
import org.proteored.miapeapi.xml.mzidentml.autogenerated.PSIPIAnalysisSearchSpectrumIdentificationItemType;
import org.proteored.miapeapi.xml.mzidentml.autogenerated.PSIPIMainMzIdentMLType;
import org.proteored.miapeapi.xml.mzidentml.autogenerated.PSIPIPolypeptideModificationType;
import org.proteored.miapeapi.xml.mzidentml.autogenerated.PSIPIPolypeptidePeptideType;
import org.proteored.miapeapi.xml.mzidentml.autogenerated.PSIPIPolypeptideSubstitutionModificationType;
import org.proteored.miapeapi.xml.mzidentml.util.MzidentmlControlVocabularyXmlFactory;
import org.proteored.miapeapi.xml.mzidentml.util.Utils;
import org.proteored.miapeapi.xml.util.MiapeXmlUtil;

public class IdentifiedPeptideImpl implements IdentifiedPeptide {
	private final PSIPIAnalysisSearchSpectrumIdentificationItemType spectrumItemXML;
	private final PSIPIPolypeptidePeptideType peptideXML;
	private static final String CV_SEARCH_ENGINE_SCORE = "MS:1001153";
	private final InputData inputData;
	private final MzidentmlControlVocabularyXmlFactory cvUtil;
	private final String spectrumRef;
	private final int identifier;
	private List<IdentifiedProtein> identifiedProteins;
	private static Logger log = Logger.getLogger("log4j.logger.org.proteored");
	private final String RT;
	private final Set<PeptideScore> scores;
	private final Map<String, IdentifiedProtein> proteinHash;
	private final PSIPIMainMzIdentMLType mzIdentML;

	public IdentifiedPeptideImpl(
			PSIPIAnalysisSearchSpectrumIdentificationItemType spectIdentItemXML,
			PSIPIPolypeptidePeptideType peptideXML,
			PSIPIMainMzIdentMLType mzIdentML, InputData inputData,
			String spectrumRef, Integer peptideID,
			ControlVocabularyManager cvManager,
			Map<String, IdentifiedProtein> proteinHash, String RT) {
		spectrumItemXML = spectIdentItemXML;
		this.peptideXML = peptideXML;
		this.mzIdentML = mzIdentML;
		this.inputData = inputData;
		cvUtil = new MzidentmlControlVocabularyXmlFactory(null, cvManager);
		identifier = peptideID;

		this.proteinHash = proteinHash;
		identifiedProteins = getProteinsFromThisPeptide();
		this.RT = RT;

		scores = getScoresFromThisPeptides(spectIdentItemXML, peptideXML,
				cvManager);
		// In case of spectrumRef is null, get it from the ID of the peptide. If
		// it is not null, no changes are made.
		this.spectrumRef = getSpectrumRefFromThisPeptide(spectrumRef);
	}

	@Override
	public String toString() {
		List<String> scoreNames = new ArrayList<String>();

		for (PeptideScore peptScore : getScores()) {
			scoreNames.add(peptScore.getName());
		}
		Collections.sort(scoreNames);
		String value = null;
		for (PeptideScore peptScore : getScores()) {
			if (peptScore.getName().equals(scoreNames.get(0)))
				value = peptScore.getValue();
		}
		return getId() + "-" + getSequence() + "(" + value + ")";
	}

	/**
	 * In case of spectrumRef is null, get it from the ID of the peptide
	 * 
	 * @param spectrumRef
	 * @return
	 */
	private String getSpectrumRefFromThisPeptide(String spectrumRef) {
		// if it is not null, not change it
		if (spectrumRef != null)
			return spectrumRef;

		String specRef = null;
		if (peptideXML != null) {
			String id = peptideXML.getId();
			if (!id.equals("")) {
				// TODO, support more formats?

				String entireSpectrumRef = id;
				// if the spectrumRef have the following format: peptide_x_y
				// where x is the query number and y is the number of candidate
				// of this query:
				String regexp = "^peptide_(\\d+)_\\d+$";

				if (Pattern.matches(regexp, entireSpectrumRef)) {
					Pattern p = Pattern.compile(regexp);
					Matcher m = p.matcher(entireSpectrumRef);
					if (m.find()) {
						specRef = m.group(1);
					}
				} else {
					specRef = id;
				}

				// if the spectrumRef have the following format:
				// IGGGEKLIVR%ACET_nterm:::::::::::%sample_0%cmpd_11555
				// where the desired ref is the number after "cmpd_"
				regexp = "^.*cmpd_(\\d+).*$";

				if (Pattern.matches(regexp, entireSpectrumRef)) {
					Pattern p = Pattern.compile(regexp);
					Matcher m = p.matcher(entireSpectrumRef);
					if (m.find()) {
						specRef = m.group(1);
					}
				} else {
					// throw new IllegalMiapeArgumentException(
					// "The spectrum ref is not recognized: "
					// + entireSpectrumRef);

				}
			}
		}
		return specRef;
	}

	private List<IdentifiedProtein> getProteinsFromThisPeptide() {
		List<IdentifiedProtein> ret = new ArrayList<IdentifiedProtein>();
		final List<PSIPIAnalysisProcessPeptideEvidenceType> peptideEvidences = spectrumItemXML
				.getPeptideEvidence();
		if (peptideEvidences != null) {
			for (PSIPIAnalysisProcessPeptideEvidenceType peptideEvidence : peptideEvidences) {

				PSIPIAnalysisSearchDBSequenceType dbSequenceXML = MiapeMSIDocumentImpl
						.getDBSequence(peptideEvidence.getDBSequenceRef(),
								mzIdentML.getSequenceCollection()
										.getDBSequence());
				PSIPIAnalysisProcessProteinDetectionHypothesisType proteinHypotesisXML = MiapeMSIDocumentImpl
						.getProteinHypothesisByPeptideEvidenceOrByDBSequence(
								peptideEvidence.getId(),
								peptideEvidence.getDBSequenceRef(), mzIdentML);
				// Only create the protein if has passed the threshold
				if ((proteinHypotesisXML != null && proteinHypotesisXML
						.isPassThreshold()) || dbSequenceXML != null) {
					Integer proteinID = MiapeXmlUtil.ProteinCounter
							.increaseCounter();
					IdentifiedProtein protein = null;
					// If the protein has been added previously, not add to the
					// general proteinhash
					if (!proteinHash.containsKey(dbSequenceXML.getAccession())) {
						protein = new IdentifiedProteinImpl(dbSequenceXML,
								proteinHypotesisXML, proteinID,
								cvUtil.getCvManager());
						// log.info("adding protein " + protein.getAccession() +
						// "/" +
						// protein.getId()
						// + " to the hash from peptide " + getSequence());
						proteinHash.put(protein.getAccession(), protein);
					} else {
						protein = proteinHash.get(dbSequenceXML.getAccession());
					}
					// new 23-May-2013
					((IdentifiedProteinImpl) protein)
							.addIdentifiedPeptide(this);

					ret.add(protein);
				}

			}
			return ret;
		}
		return null;
	}

	@Override
	public String getCharge() {
		return String.valueOf(spectrumItemXML.getChargeState());
	}

	@Override
	public String getMassDesviation() {
		StringBuilder sb = new StringBuilder();
		Double calculatedMassToCharge = spectrumItemXML
				.getCalculatedMassToCharge();
		if (calculatedMassToCharge != null) {
			sb.append(MiapeXmlUtil.CALCULATED_MZ + "=");
			sb.append(calculatedMassToCharge);
			sb.append(MiapeXmlUtil.TERM_SEPARATOR);
		}
		double experimentalMassToCharge = spectrumItemXML
				.getExperimentalMassToCharge();
		if (experimentalMassToCharge > 0) {
			sb.append(MiapeXmlUtil.EXPERIMENTAL_MZ + "=");
			sb.append(experimentalMassToCharge);
		}

		return Utils.checkReturnedString(sb);
	}

	@Override
	public Set<PeptideModification> getModifications() {
		Set<PeptideModification> modifications = new HashSet<PeptideModification>();
		// Modifications
		List<PSIPIPolypeptideModificationType> xmlModifications = peptideXML
				.getModification();
		if (xmlModifications != null) {
			for (PSIPIPolypeptideModificationType modification : xmlModifications) {
				modifications.add(new PeptideModificationImpl(modification,
						cvUtil));
			}
		}
		// Substitution modifications
		List<PSIPIPolypeptideSubstitutionModificationType> xmlSubstitutionModification = peptideXML
				.getSubstitutionModification();
		if (xmlSubstitutionModification != null) {
			for (PSIPIPolypeptideSubstitutionModificationType substitutionModification : xmlSubstitutionModification) {
				modifications.add(new PeptideModificationImpl(
						substitutionModification, cvUtil));
			}
		}
		if (modifications.size() > 0)
			return modifications;
		return null;
	}

	@Override
	public Set<PeptideScore> getScores() {
		return scores;
	}

	public static Set<PeptideScore> getScoresFromThisPeptides(
			PSIPIAnalysisSearchSpectrumIdentificationItemType spectrumItemXML,
			PSIPIPolypeptidePeptideType peptideXML,
			ControlVocabularyManager cvManager) {
		List<FuGECommonOntologyParamType> params = spectrumItemXML
				.getParamGroup();
		// Scores in the Peptide element
		params.addAll(peptideXML.getParamGroup());

		Set<PeptideScore> peptideScores = new HashSet<PeptideScore>();
		for (FuGECommonOntologyParamType param : params) {
			final Accession controlVocabularyId = cvManager
					.getControlVocabularyId(param.getName(),
							Score.getInstance(cvManager));
			if (controlVocabularyId != null) {
				final ControlVocabularyTerm cvTerm = cvManager
						.getCVTermByAccession(controlVocabularyId,
								Score.getInstance(cvManager));
				peptideScores
						.add(new PeptideScoreImpl(cvTerm, param.getValue()));
			}

		}
		return peptideScores;
	}

	@Override
	public String getSequence() {
		return peptideXML.getPeptideSequence();
	}

	@Override
	public String getSpectrumRef() {
		return spectrumRef;

	}

	@Override
	public InputData getInputData() {
		return inputData;
	}

	@Override
	public int getRank() {
		return spectrumItemXML.getRank();
	}

	@Override
	public int getId() {
		return identifier;
	}

	@Override
	public List<IdentifiedProtein> getIdentifiedProteins() {
		return identifiedProteins;

	}

	@Override
	public String getRetentionTimeInSeconds() {
		return RT;
	}

	public void addProtein(IdentifiedProteinImpl protein) {
		if (identifiedProteins == null)
			identifiedProteins = new ArrayList<IdentifiedProtein>();
		identifiedProteins.add(protein);

	}

}
